import shutil
import sys

import pandas as pd
import os
import subprocess
import random
from datetime import datetime, timedelta
import re
import datetime
from dateutil import parser

author_map = {}


def initAuthorAndDate(directory):
    # 遍历指定目录及其子目录
    encoding = 'utf-8'

    # 遍历指定目录及其子目录
    for root, dirs, files in os.walk(directory):
        # 跳过 .idea 和 .git 文件夹
        dirs[:] = [d for d in dirs if d not in {'.idea', '.git'}]
        for file in files:
            file_path = os.path.join(root, file)

            # 读取文件内容
            try:
                with open(file_path, 'r', encoding=encoding) as f:
                    content = f.read()
            except Exception as e:
                print(f"无法使用编码 {encoding} 读取文件 {file_path}: {e}，跳过")
                continue  # 跳过当前文件

            # 查找当前作者、日期和版本
            author_pattern = r'@author\s+(.*)'
            date_pattern = r'@date\s+(.*)'
            since_pattern = r'@since\s+(.*)'

            current_author_match = re.search(author_pattern, content)
            current_date_match = re.search(date_pattern, content)
            current_since_match = re.search(since_pattern, content)

            current_author = current_author_match.group(1).strip() if current_author_match else None

            # 优先提取 @date，如果没有则提取 @since
            current_date = current_date_match.group(1).strip() if current_date_match else None
            if current_date is None and current_since_match:
                current_date = current_since_match.group(1).strip()

            # 检查作者是否需要替换
            if current_author is not None or current_date is not None:
                #print(f"{file_path} 文件  {current_author}   {current_date}")
                author_map[file_path] = (current_author, current_date)


def replace_author_and_date(path, new_author, new_date, commit_date):
    # 检查路径是文件还是目录
    if os.path.isfile(path):
        # 如果是文件，处理该文件
        process_file(path, new_author, new_date, commit_date)
    elif os.path.isdir(path):
        # 如果是目录，遍历所有文件
        for root, dirs, files in os.walk(path):
            for file in files:
                file_path = os.path.join(root, file)
                process_file(file_path, new_author, new_date, commit_date)
    else:
        print(f"路径 '{path}' 不是有效的文件或目录。")


def process_file(file_path, new_author, new_date, commit_date):
    encoding = 'utf-8'

    # 读取文件内容
    try:
        with open(file_path, 'r', encoding=encoding) as f:
            content = f.read()
    except Exception as e:
        print(f"无法使用编码 {encoding} 读取文件 {file_path}: {e}，跳过")
        return  # 跳过当前文件

    # 查找当前作者和日期
    author_pattern = r'@author\s+(.*)'
    current_author_match = re.search(author_pattern, content)
    current_author = current_author_match.group(1).strip() if current_author_match else None

    # 检查作者是否需要替换
    if current_author is not None:
        if 'create by huangjie' in content:
            return
        content = re.sub(author_pattern, f'@author {new_author}', content)
        if commit_date == new_date:
            print(f"替换文件 '{file_path}' 中的作者名称成功: '{current_author}' -> '{new_author}'  新日期：'{new_date}'")

    # 替换日期
    content = re.sub(r'@date\s+.*', f'@date {new_date}', content)
    content = re.sub(r'@since\s+.*', f'@date {new_date}', content)

    # 写入修改后的内容到文件
    try:
        with open(file_path, 'w', encoding=encoding) as f:
            f.write(content)
    except Exception as e:
        print(f"无法写入文件 {file_path}: {e}，跳过")

def get_commit_records(directory):
    """获取Git提交记录并返回DataFrame"""
    result = subprocess.run(
        ['git', '-C', directory, 'log', '--pretty=format:%H,%cd,%an,%s', '--date=iso'],
        capture_output=True,
        text=True,
        encoding='utf-8'  # 确保使用正确的编码
    )

    # 检查命令是否成功执行
    if result.returncode != 0:
        print(f"获取提交记录失败: {result.stderr}")
        return pd.DataFrame()  # 返回空的DataFrame以避免后续错误

    commit_lines = result.stdout.strip().split('\n')

    commit_data = []
    for line in commit_lines:
        if line:
            # 分割日期，作者和提交信息
            commit_hash, date_str, author, message = line.split(',', 3)
            # 过滤掉以"Merge branch"开头的提交记录
            if message.startswith("Merge"):
                print(f"跳过: {message}")
                continue

            # 使用dateutil解析日期字符串
            try:
                date = parser.parse(date_str)
                commit_data.append({'提交时间': date, '姓名': author, '提交备注': message, "git提交记录id": commit_hash})
            except ValueError as e:
                print(f"日期格式错误: {e}")
                continue

    # 创建DataFrame并按提交时间升序排序
    df = pd.DataFrame(commit_data)
    df.sort_values(by='提交时间', ascending=True, inplace=True)
    df['提交时间'] = df['提交时间'].dt.strftime('%Y-%m-%d %H:%M:%S')  # 格式化日期为字符串
    return df


def save_commit_records_to_excel(base_directory, df):
    """将提交记录保存为Excel文件"""
    base_directory2 = os.path.join(base_directory, "git_template")
    output_file = os.path.join(base_directory2, 'old_commit_records.xlsx')
    df.to_excel(output_file, index=False)
    print(f"提交记录已保存到: {output_file}")


def generate_random_time(current_date, previous_time=None, start_hour=8, start_minute=30, end_hour=18, end_minute=30):
    """生成08:30到18:30之间的随机时间，日期为current_date。
    如果current_date与上一条记录相同，生成的时间不能超过上一条记录的时分秒。"""

    # 确保current_date是datetime对象
    if isinstance(current_date, pd.Timestamp):
        current_date = current_date.to_pydatetime()

    # 生成开始和结束时间
    start_time = current_date.replace(hour=start_hour, minute=start_minute, second=0, microsecond=0)
    end_time = current_date.replace(hour=end_hour, minute=end_minute, second=0, microsecond=0)

    # 如果有上一条记录的时间，且日期相同，则设置新的结束时间为上一条记录的时间
    if previous_time is not None and current_date.date() == previous_time.date():
        start_time = max(start_time, previous_time)

    # 生成随机时间
    random_time = start_time + timedelta(minutes=random.randint(0, (end_time - start_time).seconds // 60))

    return random_time


def read_excel(file_path):
    """读取Excel文件并返回数据框"""
    return pd.read_excel(file_path)


def validate_data(df):
    """验证数据格式"""
    errors = []

    for index, row in df.iterrows():
        # 检查提交时间格式
        try:
            pd.to_datetime(row['提交时间'])
        except ValueError:
            errors.append((index + 1, '提交时间', '格式不正确'))

        # 检查姓名是否为空
        if not row['姓名']:
            errors.append((index + 1, '姓名', '不能为空'))

        # 检查提交备注是否为空
        if not row['提交备注']:
            errors.append((index + 1, '提交备注', '不能为空'))

    return errors


def set_git_config(directory, name, email):
    """设置git的用户名和邮箱"""
    subprocess.run(['git', '-C', directory, 'config', 'user.name', name])
    subprocess.run(['git', '-C', directory, 'config', 'user.email', email])


def get_committed_files(directory):
    """获取已提交的文件列表"""
    #result = subprocess.run(['git', '-C', directory, 'ls-files', '-m'], stdout=subprocess.PIPE, text=True)
    #return set(result.stdout.splitlines())
    # 执行 git status --porcelain 命令
    result = subprocess.run(['git', '-C', directory,  'status', '--porcelain'], stdout=subprocess.PIPE, text=True)

    # 解析输出
    modified_files = []
    for line in result.stdout.splitlines():
        line = line.strip()
        if line.startswith(('A', 'M', '??')):  # 只关注新增和修改的文件
            # 找到第一个空格的位置
            space_index = line.find(' ')
            if space_index != -1:
                file_name = line[space_index + 1:].strip()
                modified_files.append(file_name)  # 提取文件名（去掉状态标识）

    return modified_files

def parse_gitignore(directory):
    """解析.gitignore文件，返回需要忽略的文件和目录"""
    ignore_file = os.path.join(directory, '.gitignore')
    ignored_files = set()

    if os.path.exists(ignore_file):
        with open(ignore_file, 'r') as f:
            for line in f:
                line = line.strip()
                if line and not line.startswith('#'):  # 忽略空行和注释
                    ignored_files.add(line)
    return ignored_files


def get_all_files(directory, excluded_patterns, ignored_files):
    """获取指定目录及其子目录下的所有文件，排除指定目录和.gitignore中的文件"""
    all_files = []
    for dirpath, _, filenames in os.walk(directory):
        for filename in filenames:
            full_path = os.path.join(dirpath, filename)
            # 检查是否在排除模式中或.gitignore中
            if any(pattern in dirpath for pattern in excluded_patterns) or filename in ignored_files:
                continue
            all_files.append(full_path)
    return all_files


def commit_files(directory, branch, commit_message, commit_time):
    """提交选择的文件到git，并设置提交时间"""
    commit_time = commit_time.strftime('%Y-%m-%d %H:%M:%S')
    subprocess.run(['git', '-C', directory, 'add', '.'])
    subprocess.run(['git', '-C', directory, 'commit', '-m', commit_message, '--date', commit_time])
    subprocess.run(['git', '-C', directory, 'push', '-f', 'origin', branch])

def parse_selection(selection, max_index):
    """解析用户输入的文件选择，包括范围和单个选择"""
    selected_files = set()
    for part in selection.split(','):
        part = part.strip()
        if '-' in part:
            start, end = part.split('-')
            try:
                start = int(start) - 1
                end = int(end) - 1
                selected_files.update(range(max(0, start), min(max_index, end + 1)))
            except ValueError:
                print(f"无效范围: {part}")
        else:
            try:
                idx = int(part) - 1
                if 0 <= idx < max_index:
                    selected_files.add(idx)
            except ValueError:
                print(f"无效输入: {part}")
    return selected_files


def get_valid_file_selection(files):
    """获取用户有效的文件选择"""
    while True:
        selected_input = input("\n请输入要提交的文件编号（用逗号分隔，例如 1,2 或 1-10），或输入 'exit' 跳过本次提交：")
        if selected_input.lower() == 'exit':
            return None  # 返回菜单

        if selected_input.strip() == "":
            print("已跳过此次提交。")
            return None  # 直接按回车，跳过此次提交

        selected_indices = parse_selection(selected_input, len(files))
        if selected_indices:
            return [files[i] for i in selected_indices]
        else:
            print("没有选择任何有效文件，请重新输入。")


def list_commits(directory):
    """列出所有待推送的提交"""
    result = subprocess.run(['git', '-C', directory, 'log', '--oneline'], capture_output=True, text=True)
    return result.stdout.strip()


def generate_templates():
    """生成Excel模板和Git用户信息模板"""
    # 生成提交记录模板
    commit_template_data = {
        '提交时间': [],
        '姓名': [],
        '提交备注': []
        , 'git提交记录id': []
    }
    commit_df = pd.DataFrame(commit_template_data)
    commit_df.to_excel('git_template/commit_records.xlsx', index=False)

    # 生成Git用户信息模板
    user_template_data = {
        '姓名': ['你的姓名'],
        '邮箱': ['你的邮箱']
    }
    user_df = pd.DataFrame(user_template_data)
    user_df.to_excel('git_template/git_user_info_template.xlsx', index=False)

    print("模板已生成：")
    print("- 提交记录模板: git_template/commit_records.xlsx")
    print("- Git用户信息模板: git_template/git_user_info_template.xlsx")


def get_nth_record(df, n):
    """获取 DataFrame 的第 n 条记录"""
    if n < 0 or n >= len(df):
        print("索引超出范围，请输入一个有效的索引。")
        return None
    return df.iloc[n]


def set_system_time(time_string):
    # 将字符串转换为 datetime 对象
    new_time = datetime.datetime.strptime(time_string, '%Y-%m-%d %H:%M:%S')

    # 格式化时间为 'HH:MM:SS'
    formatted_time = new_time.strftime('%H:%M:%S')
    # 格式化日期为 'MM-DD-YY'（使用两位年份）
    formatted_date = new_time.strftime('%y-%m-%d')

    # 设置时间
    try:
        # 使用 cmd 执行 time 和 date 命令
        subprocess.run(['cmd', '/c', 'time', formatted_time], check=True)
        subprocess.run(['cmd', '/c', 'date', formatted_date], check=True)
        print(f"系统时间已设置为: {new_time}")
    except subprocess.CalledProcessError as e:
        print(f"设置时间失败: {e}")
        sys.exit(1)  # 退出程序


def execute_git_commit_task(history_directory, directory, excluded_patterns):
    """执行GIT提交任务"""
    # 存储文件名和作者名称的字典

    excel_file = 'commit_records.xlsx'  # Excel文件路径
    user_info_file = 'git_user_info_template.xlsx'  # 用户信息文件路径

    df = read_excel(excel_file)
    user_info_df = read_excel(user_info_file)

    # 验证数据格式
    errors = validate_data(df)
    if errors:
        print("数据格式检查发现以下问题：")
        for line, column, error in errors:
            print(f"第 {line} 行: {column} {error}")
        return  # 退出程序，不继续执行

    ignored_files = parse_gitignore(directory)

    # 获取用户输入的起始索引
    user_input = input("请输入从第几条记录开始遍历（按回车从头开始，1开始计数）：")
    # 判断用户是否输入了内容
    if user_input.strip() == "":
        start_index = 0  # 回车表示从头开始
    else:
        start_index = int(user_input)-1
        print(f"第{start_index}条记录：{get_nth_record(df, start_index)}")
        yes = input("请按回车确认")

    # 要提交的分支
    branch = "master"

    previous_commit_time = None  # 初始化上一条提交时间
    for index, row in df.iterrows():
        if index >= start_index:
            commit_time = row['提交时间']
            commit_time = generate_random_time(commit_time, previous_commit_time)
            previous_commit_time = commit_time
            name = row['姓名']
            git_id = row['git提交记录id']

            # 从用户信息中获取邮箱
            user_info_row = user_info_df[user_info_df['姓名'] == name]
            if user_info_row.empty:
                print(f"姓名 '{name}' 在用户信息中未找到。")
                continue
            email = user_info_row['邮箱'].values[0]
            commit_message = row['提交备注']

            print(f"\n-----准备提交记录: {index + 1}-----")
            print(f"提交时间: {commit_time}")
            print(f"姓名: {name}")
            print(f"邮箱: {email}")
            print(f"提交备注: {commit_message}")
            print(f"来源git提交记录id：{git_id}")
            print(f"-------------------")
            # 设置git配置
            set_git_config(directory, name, email)
            # 提交选择的文件，带上指定的提交时间
            set_system_time(str(commit_time))

            if str(git_id) != 'nan':
                if history_directory and os.path.isdir(history_directory):
                    print("源目录正在更新最新代码...")
                    subprocess.run(['git', '-C', history_directory, 'pull'], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                    print(f"源目录已更新到最新代码!正在checkout到{git_id}")
                    subprocess.run(['git', '-C', history_directory, 'reset', '--hard', git_id], check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                    print(f"源目录已checkout代码到{git_id}")
                    # 获取所有文件（包括已提交和未提交的文件）
                    all_files = get_all_files(history_directory, excluded_patterns, parse_gitignore(history_directory))
                    #print(f"获取到源目录所有文件：{all_files}")

                    # # 删除目标目录中比 history_directory 多出来的文件
                    # for dirpath, _, filenames in os.walk(directory):
                    #     for filename in filenames:
                    #         dst_file = os.path.join(dirpath, filename)
                    #         relative_path = os.path.relpath(dst_file, directory)
                    #         src_file = os.path.join(history_directory, relative_path)
                    #
                    #         # 检查文件是否在历史目录中不存在且不在排除的文件夹中
                    #         if not os.path.exists(src_file) and not any(
                    #                 excluded in relative_path for excluded in ['.git', 'git_template', '.idea']):
                    #             try:
                    #                 os.remove(dst_file)
                    #                 print(f"已删除多余文件: {dst_file}")
                    #             except PermissionError as e:
                    #                 print(f"无法删除文件 {dst_file}: {e}")

                    # 复制所有文件到目标目录
                    for file in all_files:
                        file = os.path.relpath(file, history_directory)
                        src_file = os.path.join(history_directory, file)
                        dst_file = os.path.join(directory, file)

                        # 确保目标目录存在
                        os.makedirs(os.path.dirname(dst_file), exist_ok=True)

                        # 如果目标文件存在，删除它以便覆盖
                        if os.path.exists(dst_file):
                            os.remove(dst_file)

                        # 复制文件
                        if os.path.exists(src_file):
                            shutil.copy2(src_file, dst_file)
                            dst_file = str(dst_file)
                            if dst_file in author_map:
                                commit_author, commit_time_str = author_map[dst_file]
                                # print(f"文件{dst_file}之前已经提交过，使用第一次提交的作者{commit_author}和日期{commit_time_str}")
                            else:
                                commit_time_str = commit_time.strftime('%Y-%m-%d')
                                commit_author = email.split('@')[0]

                            replace_author_and_date(dst_file, commit_author, commit_time_str,
                                                    commit_time.strftime('%Y-%m-%d'))
                            author_map[dst_file] = (commit_author, commit_time_str)
                            #print(f"已复制文件: {src_file} 到 {dst_file}")

                    # 获取有变化的文件
                    committed_files = get_committed_files(directory)

                    if len(committed_files) > 0:
                        print(f"文件发生变化：{committed_files}")
                        # 确认是否有问题
                        confirmation = input("请检查作者、代码、日期等是否正确，按回车键确认，提交这些文件，或输入 'exit' 跳过本次提交: ")
                        #confirmation = ""

                        if confirmation.lower() == 'exit':
                            print("已跳过本次提交。")
                        else:
                            print(f"正在提交到{branch}分支...")
                            # 提交所有文件，带上指定的提交信息和时间
                            commit_files(directory, branch, commit_message, commit_time)  # 提交信息和时间可以根据需要调整
                            print(f"文件已成功提交到{branch}。")
                    else:
                        print("无文件发生变化，跳过提交")
                        # 确认是否有问题
                        confirmation = input("请检查作者、代码、日期等是否正确，按回车键确认，提交这些文件，或输入 'exit' 跳过本次提交: ")
                        if confirmation.lower() == 'exit':
                            print("已跳过本次提交。")
                else:
                    print("历史目录无效，跳过检出步骤。")
                    return
            else:
                # 获取有变化的文件
                committed_files = get_committed_files(directory)

                print("\n当前目录下的未提交文件:")
                for i, file in enumerate(committed_files):
                    print(f"{i + 1}: {file}")

                refresh = input("是否需要刷新一次目录：(y/n)")
                while refresh == 'y':
                    committed_files = get_committed_files(directory)
                    refresh = input("是否需要刷新一次目录：(y/n)")

                # 用户选择文件
                selected_files = get_valid_file_selection(committed_files)
                if selected_files is None:
                    print("没有选择文件跳过此次提交。")
                    continue  # 跳过此次提交，继续下一个记录

                print(f"选择的文件: {selected_files}")
                for dir_item in selected_files:
                    commit_time_str = commit_time.strftime('%Y-%m-%d')
                    replace_author_and_date(dir_item, email.split('@')[0], commit_time_str)

                print(f"正在提交到{branch}分支...")
                commit_files(directory, branch, commit_message, commit_time)
                print(f"文件已成功提交到{branch}。")

    # 确认是否推送
    confirm_finish = input("\n所有操作已完成！：")



def main():

    # 选择工作目录
    directory = "E:\\Desktop\\demo\\oris_c"  # 请根据需要修改路径
    directory = input("请输入要造的Git目录：")
    excluded_patterns = ["face", "/git_template", ".git", ".idea"]  # 请根据需要修改排除的目录和文件
    if not os.path.isdir(directory):
        print("无效的目录，请检查路径。")
        return
    initAuthorAndDate(directory)
    while True:
        print("\n请选择操作：")
        print("1. 生成空excel模板")
        print("2. 执行GIT提交任务")
        print("3. 根据现有项目生成提交记录excel模板")
        print("4. 退出")

        choice = input("请输入选项 (1/2/3): ")

        if choice == '1':
            generate_templates()
        elif choice == '2':
            history_directory = input("请输入要遍历的git目录，如果不需要，则直接按回车：")
            #history_directory = 'E:\Desktop\project\oris'
            execute_git_commit_task(history_directory, directory, excluded_patterns)
        elif choice == '3':
            commit_directory = input("请输入要获取提交记录的Git目录：")
            if not os.path.isdir(commit_directory):
                print("无效的目录，请检查路径。")
                continue

            commit_records_df = get_commit_records(commit_directory)
            if not commit_records_df.empty:
                save_commit_records_to_excel(directory, commit_records_df)
            else:
                print("没有获取到任何提交记录。")
        elif choice == '4':
            replace_dir = input("请输入要替换作者和日期的文件：")
            replace_author_and_date(replace_dir, "laichuanju22s", "2024-01-07")
        elif choice == '0':
            print("退出程序。")
            break
        else:
            print("无效选项，请重新输入。")


if __name__ == "__main__":
    main()
